WIP: OBLS-837 Add discrete picking order list screen - #430
Conversation
Add a new "Discrete Picking" Dashboard entry that lists all open orders ready for picking across every queue type, with real-time search and queue-type filter chips. Tapping an order seeds the picking session and reuses the existing Pick Location flow, one order at a time. - apis/picking: allow getPickTasksApi to run unfiltered; add getOpenPickTasksApi for all open (PENDING/PICKING) tasks - redux: add getOpenPickTasksAction + saga + watcher - types: add DiscretePickingOrder view model - PickingContext: add startOrderSession(requisitionId) to seed a single-order session via getPickTasksByRequisitionAction - screens/Picking: add DiscretePickingListScreen, order card, skeleton, styles, and grouping/sort/filter helpers - wire route in Main and add Dashboard entry The multi-field filter panel (Destination/Queue Type/Assignee/Status) is intentionally deferred to a follow-up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011zmhJ6yv58QsHzj8EwAnZb
Build an APK and deploy it to Appetize for every pull request targeting develop, so reviewers can preview changes in the browser. - Enable the pull_request_target_branch: develop trigger (was disabled), routing PR builds through the existing android-only workflow - Point appetize-deploy at a stable shared preview app via APPETIZE_APP_ID so the preview URL stays constant across builds (falls back to creating a new app per build when the var is unset) Requires (Bitrise dashboard, one-time): enable Pull Request builds for the app, and set APPETIZE_APP_ID to a preview app's public key. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011zmhJ6yv58QsHzj8EwAnZb
The appetize-deploy step's input is public_key, not app_id; the previous key was ignored, so every build created a new Appetize app. Use public_key: $APPETIZE_PUBLIC_KEY so builds update one stable preview app. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011zmhJ6yv58QsHzj8EwAnZb
Remove the public_key input added earlier. With APPETIZE_PUBLIC_KEY unset it resolved to empty, and this appetize-deploy version may mishandle an empty public_key. Match the proven develop config (no public_key input), which creates a new preview app per build. The stable shared-app key can be reintroduced once the pipeline is green and a key exists. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011zmhJ6yv58QsHzj8EwAnZb
PR builds now deploy to a dedicated PR preview Appetize app instead of overwriting the main app used by develop. A script selects the target public key by build type ($BITRISE_PULL_REQUEST distinguishes PR from push builds) and appetize-deploy uses it. A run_if guard skips the deploy entirely when no key is configured, so it never falls back to creating a new app. Requires two Bitrise env vars: APPETIZE_DEVELOP_PUBLIC_KEY (main app) and APPETIZE_PR_PUBLIC_KEY (PR preview app). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011zmhJ6yv58QsHzj8EwAnZb
Set APPETIZE_DEVELOP_PUBLIC_KEY to the main app used by develop. Leave APPETIZE_PR_PUBLIC_KEY empty until the PR preview app is created; the appetize-deploy run_if guard skips the deploy while it is empty, so PR builds cannot overwrite the main app in the meantime. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011zmhJ6yv58QsHzj8EwAnZb
The select-app script runs under set -u, so referencing an unset
APPETIZE_*_PUBLIC_KEY aborted the step (as happened before the keys were
defined, and would recur if Bitrise omits an empty-valued env var). Use
${VAR:-} defaults so the script tolerates unset or empty keys; the
run_if guard on appetize-deploy still skips the deploy when the resolved
key is empty.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011zmhJ6yv58QsHzj8EwAnZb
On PR builds only, append "(Experimental)" to the Android app_name after branding runs, so the PR preview app is clearly distinguishable from the stable build in Appetize (which shows the app name from the APK manifest). Guarded by BITRISE_PULL_REQUEST so stable/develop builds keep the plain name. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011zmhJ6yv58QsHzj8EwAnZb
Appetize keys apps by applicationId, so a same-package PR APK merged into the stable app instead of creating a separate one. On PR builds, suffix the applicationId with .experimental (alongside the app-name tag) so the PR build uploads as its own Appetize app with its own URL. No package-tied services (Firebase/Maps/google-services) exist, so the suffix is safe. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011zmhJ6yv58QsHzj8EwAnZb
Set APPETIZE_PR_PUBLIC_KEY to the PR preview app's key so PR builds now deploy the experimental APK to their own Appetize app instead of skipping the deploy. For the v1 API the appetize-deploy step uses, the build id and public key are interchangeable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011zmhJ6yv58QsHzj8EwAnZb
After deploying, POST a note to the Appetize v1 API for the deployed app with PR number, branch, short commit, and build number, so each preview is self-documenting in the dashboard. The appetize-deploy step can't set notes itself. The v1 API has no tags field, so tags stay manual. The call is non-fatal so a note failure never reds the build. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011zmhJ6yv58QsHzj8EwAnZb
There was a problem hiding this comment.
Pull request overview
Adds a new “Discrete Picking” dashboard entry and screen that lists open pickable orders (grouped from open pick tasks) and starts a single-order picking session that reuses the existing Pick Location → … → Staging flow. Also updates the picking API layer/sagas to support fetching open tasks across all queue types and adjusts Bitrise to build/deploy PR previews to Appetize.
Changes:
- Introduces Discrete Picking list UI (cards, skeleton/loading, search + queue-type chips) and wires it into navigation + dashboard.
- Adds data-layer support for “open pick tasks” (API + Redux action/saga/watcher) and a
DiscretePickingOrderview model with grouping/sorting/filtering helpers. - Updates Bitrise PR triggers and Appetize deploy behavior (shared preview keys + PR build annotation).
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/types/picking.ts | Adds DiscretePickingOrder view model for the discrete picking list. |
| src/screens/Picking/PickingContext.tsx | Adds startOrderSession(requisitionId) to seed a single-order picking session. |
| src/screens/Picking/discretePickingStyles.ts | New styles for discrete picking list/cards/chips. |
| src/screens/Picking/DiscretePickingOrderCard.tsx | New order card component for discrete picking list. |
| src/screens/Picking/DiscretePickingListScreen.tsx | New discrete picking list screen (fetch, search, queue chips, navigation into picking flow). |
| src/screens/Picking/discretePickingLib.ts | Groups tasks into orders; sorts and filters; computes chip counts. |
| src/screens/Picking/DiscretePickingCardSkeleton.tsx | Loading skeleton card for the list. |
| src/screens/Dashboard/dashboardData.ts | Adds “Discrete Picking” dashboard entry. |
| src/redux/sagas/picking.ts | Adds saga + watcher for fetching open pick tasks. |
| src/redux/actions/picking.ts | Adds GET_OPEN_PICK_TASKS action trio + action creator. |
| src/Main.tsx | Adds route for DiscretePickingList. |
| src/apis/picking.ts | Makes pick-tasks query params optional; adds getOpenPickTasksApi (status=PENDING/PICKING). |
| .bitrise/bitrise.yml | Enables PR-to-develop builds + Appetize deploy targeting/annotation + PR “experimental” package/name tweak. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| getPickTasksByRequisitionAction(requisitionId, (res) => { | ||
| if ('errorMessage' in res || !res.response?.data) { | ||
| Alert.alert('Error', 'Failed to load pick tasks for the selected order.'); | ||
| resolve(false); | ||
| return; | ||
| } | ||
|
|
||
| const orderTasks = res.response.data; |
| else | ||
| NOTE="${BITRISE_GIT_BRANCH:-} - ${SHA} - build ${BITRISE_BUILD_NUMBER:-}" | ||
| fi | ||
| BODY="$(jq -n --arg note "$NOTE" '{note: $note}')" |
- startOrderSession: surface the callback's errorMessage in the alert (consistent with startSession) instead of a generic message, so network/auth/backend failures are distinguishable. - Appetize note step: build the JSON body with printf/sed instead of jq, so the step is self-contained and doesn't fail if the skippable jq brew-install was skipped. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011zmhJ6yv58QsHzj8EwAnZb
| # applicationId, so the suffix makes the PR build a separate Appetize app | ||
| # (its own URL) instead of overwriting the stable app. Runs after branding | ||
| # (which sets app_name) and before the release assemble. | ||
| - script@1: |
There was a problem hiding this comment.
I think this could be a part of bundle::release-steps-android: {} or the bundle::setup-steps-android: {}
| # Build a preview (APK + Appetize deploy) for every pull request into develop. | ||
| - pull_request_target_branch: develop | ||
| enabled: false | ||
| workflow: android-only |
There was a problem hiding this comment.
This will now trigger a build on each PR to the develop branch. Do we want to have it? I think I'd prefer to have a different approach here. I'd make a preview build when the code is pushed to the preview/* branch (or something like that). Not everything (eg a commit with a typo fix after review) needs a publish to Appetize and to be sent out to Bitrise build recipients. Plus, not that long ago we ran into a credits limit due to building on each PR to develop.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (3)
src/screens/Picking/PickingContext.tsx:114
startOrderSessiondoesn't handle the API's{ errorCode, message }error shape (used elsewhere in the picking flow). If the requisition fetch returnserrorCodewithout throwing, this will currently fall through and may show a generic error or attempt to use an undefined task list.
getPickTasksByRequisitionAction(requisitionId, (res) => {
if (res.errorMessage || !res.response?.data) {
Alert.alert('Error', res.errorMessage ?? 'Failed to load pick tasks for the selected order.');
resolve(false);
return;
}
const orderTasks = res.response.data;
src/screens/Picking/DiscretePickingListScreen.tsx:47
- The open-order fetch callback ignores backend
{ errorCode, message }responses (which this codebase uses elsewhere in picking). If the endpoint returnserrorCodewith a 200, this screen will silently show an empty state instead of reporting the failure.
getOpenPickTasksAction(({ response, errorMessage }) => {
if (!errorMessage && response?.data) {
setTasks(response.data);
}
setIsRefreshing(false);
setHasLoaded(true);
})
src/screens/Picking/DiscretePickingListScreen.tsx:3
fetchOrdersshould surface API failures to the user (this file currently doesn't import Alert, so there’s no way to show the error inline like other picking screens do). AddingAlertenables consistent error handling in the fetch callback below.
This issue also appears on line 41 of the same file.
import { FlatList, ScrollView, View } from 'react-native';
awalkowiak
left a comment
There was a problem hiding this comment.
I am merging as is so we can QA this tomorrow. The bitrise comments will be addressed separately if needed
Summary
Adds a new Discrete Picking entry to the Dashboard that lists all open orders ready for picking across every queue type, with real-time search and queue-type filter chips. Tapping an order seeds the picking session and reuses the existing Pick Location → … → Staging flow, one order at a time. This is the "one order at a time" counterpart to the existing batch Picking flow.
Implements OBLS-837.
Changes
Feature
getPickTasksApinow runs unfiltered (params optional); addedgetOpenPickTasksApito fetch all open (PENDING/PICKING) pick tasks across all queue types, plusgetOpenPickTasksActionaction/saga/watcher.DiscretePickingOrderview model.PickingContext.startOrderSession(requisitionId)seeds a single-order session via the existinggetPickTasksByRequisitionAction, then navigation continues into the existing Pick Location flow.src/screens/Picking/) —DiscretePickingListScreen,DiscretePickingOrderCard,DiscretePickingCardSkeleton,discretePickingStyles, anddiscretePickingLib(group-by-requisition, priority sort, real-time filter, chip counts). ReusesBarcodeSearchHeader,ListLoadingSkeleton,EmptyView,emptyStateMessage.Main.tsx; entry indashboardData.ts.CI / preview
pull_request_target_branch: developtrigger so PRs build an APK and deploy to Appetize for preview.appetize-deployat a stable shared preview app via$APPETIZE_PUBLIC_KEY(falls back to creating a new app per build when unset).Notes / follow-ups
pick-tasksendpoint returning all open tasks whendeliveryTypeCode/ordersCountare omitted (filtered here tostatus=PENDING&status=PICKING). Confirm this behavior on the backend.PickingPickType/staging on completion; adjusting the return destination for discrete picks is a possible follow-up.Testing
yarn lintclean; app-leveltscclean (remainingtscerrors are pre-existing@react-navigationlib-def parse issues undernode_modules, unrelated to this change).🤖 Generated with Claude Code
Generated by Claude Code